/*        
 * File:   proto.cpp
 * Author: saulio
 *
 * Created on July 15, 2012, 5:57 PM
 */

    
#include<map>
#include <fstream>
#include <locale>
#include <sys/msg.h>
#include <sys/types.h>
#include <iostream>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include "Frame.h"

const char PUSH_ACK=24;
const char RST_ACK=20;
const char SYNC_ACK=18;
const char FIN_ACK=17;

typedef struct tcp_dat 
{
    char        flag;
    char        ip_s[16];
    char        port_s[6];
    char        ip_d[16];
    char        port_d[6]; 
    char        ack[11];
    char        seq[11];
    char        n_seq[11];
};

typedef struct counters
{
    int         n;
    int         s;
    int         c;
};

typedef struct seq_track
{
    seq_track(int ls): last_seq(ls){}
    int         last_seq;
    short       rto;
    
};

unsigned char domain;
void getcpdat(char*,tcp_dat*,counters*,bool&); 
void package_assambler(tcp_dat*,Message_queue&);

int main (int argc, char** argv){
    
  domain=unsigned(char(atoi(argv[2])));
  int unpacker_queue_id=Message_queue_setup(C_UNPACKER_QUEUE_KEY);
  int log_queue_id=Message_queue_setup(LOGGER_QUEUE_KEY);
  Message_queue log_msg;
  std::ifstream ifs (argv[3]);
 
  if (unpacker_queue_id==EXIT_FAILURE and log_queue_id!=EXIT_FAILURE){
      bzero((char *) &log_msg, sizeof(log_msg));
      log_msg.id_message=LOG_QUEUE_ERR_UNPACKER;
      msgsnd(log_queue_id, (struct msgbuf *)&log_msg,
      sizeof(log_msg)-sizeof(long),IPC_NOWAIT);
      ifs.close();
      return(EXIT_FAILURE);
  }
  else{
        
        std::locale loc;
        const std::collate<char>& coll = std::use_facet<std::collate<char> >(loc);
        std::map<long,seq_track>::iterator it;
        std::map<long,seq_track>::iterator ti;
        std::map<long,seq_track> list;
        std::pair<std::map<long,seq_track>::iterator,bool>ret;
        
        long myhash, youhash;
        Message_queue trigger;
        tcp_dat check;
        counters count;
        std::string cid,csid;
        bool flag=false, l=true;
        int pos=0,ref=0,exchange=0;
        char line[512]={0};
        ifs.seekg (0, std::ios::end);
        ref = ifs.tellg();
        ifs.seekg (0, std::ios::beg);

        while(true){
                while (ifs.good() and ref-1>pos){
                    bzero((char *) &trigger, sizeof(trigger));
                    bzero((char *) &check, sizeof(check));
                    ifs.getline(line,512);
                    if(ifs.gcount()>29){
                    getcpdat(line,&check,&count,flag);
                    cid=check.ip_d;
                    cid+=check.port_d;
                    myhash = coll.hash(cid.data(),cid.data()+cid.length());
                    if(check.flag==SYNC_ACK)
                    {
                        it=list.begin();
                        list.insert(std::pair<long,seq_track>(myhash,atoi(check.seq)));
                    }
                    else if(check.flag==FIN_ACK)
                    {
                        csid=check.ip_s;
                        csid+=check.port_s;
                        youhash = coll.hash(csid.data(),csid.data()+csid.length());
                        list.erase(myhash);
                        list.erase(youhash);
                    }   
                    else if(check.flag==RST_ACK)
                    {
                        csid=check.ip_s;
                        csid+=check.port_s;
                        youhash = coll.hash(csid.data(),csid.data()+csid.length());
                           
                        it=list.find(myhash);
                        ti=list.find(youhash);
                        if(it==list.end() and ti==list.end())
                        {
                            package_assambler(&check,trigger);
                            trigger.package.header.type_message=GUARD_REPORT;
                            trigger.package.header.version=version;
                            trigger.package.header.domain=domain;
                            trigger.id_message=GR;
                            msgsnd(unpacker_queue_id, (struct msgbuf*)&trigger,
                            sizeof(trigger)-sizeof(long),IPC_NOWAIT);
                            
                            bzero((char *) &log_msg, sizeof(log_msg));
                            log_msg.id_message=LOG_QUEUE_EVNT_GUARD;
                            log_msg.package=trigger.package;
                            log_msg.cli_addr=trigger.cli_addr;
                            msgsnd(log_queue_id, (struct msgbuf *)&log_msg,
                            sizeof(log_msg)-sizeof(long),IPC_NOWAIT);
                            std::cout<< inet_ntoa(trigger.cli_addr.sin_addr)<<std::endl;
                            std::cout<< inet_ntoa(log_msg.cli_addr.sin_addr)<<std::endl;
                      
                         }
                         else
                         {
                            list.erase(myhash);
                            list.erase(youhash);
                         }
                            
                    }
                    else if (check.flag==PUSH_ACK)
                    {
                        it=list.find(myhash);
                        if((*it).second.last_seq==atoi(check.seq)){
                            (*it).second.rto++;
                        }
                        else{
                            (*it).second.last_seq=atoi(check.seq);
                            (*it).second.rto=0;
                        }
                        if((*it).second.rto==5)
                        {
                            (*it).second.rto=0;
                            package_assambler(&check,trigger);
                            trigger.package.header.type_message=MISSING_REPORT;
                            trigger.package.header.version=version;
                            trigger.package.header.domain=domain;
                            trigger.id_message=MR;
                            trigger.package.next_sequence_number=atoi(check.n_seq);
                            msgsnd(unpacker_queue_id, (struct msgbuf*)&trigger,
                            sizeof(trigger)-sizeof(long),IPC_NOWAIT);
                            
                            bzero((char *) &log_msg, sizeof(log_msg));
                            log_msg.id_message=LOG_QUEUE_EVNT_MISSING;
                            log_msg.package=trigger.package;
                            log_msg.cli_addr=trigger.cli_addr;
                            msgsnd(log_queue_id, (struct msgbuf *)&log_msg,
                            sizeof(log_msg)-sizeof(long),IPC_NOWAIT);
                            
                        }
                    }
                    pos = ifs.tellg();
                }}
                if (l){
                    for (it=list.begin() ; it != list.end(); it++)
                    std::cout << (*it).first << "=>" << (*it).second.last_seq << std::endl;
                    l=false;
                }
                
                usleep(10000);
                ifs.clear();
                ifs.seekg(0,std::ios::end);
                pos = ifs.tellg();
              
                if(pos>ref){
                    ifs.seekg(ref);   
                    exchange=pos;
                    pos=ref;
                    ref=exchange;
                    l=true;
                    ifs.clear();
                 }
                    
        }
              
  }
  
}

void getcpdat(char*cadena,tcp_dat*check,counters*count,bool&flag)
{
   flag=true;
   count->n=0;
   count->s=0;
   count->c=0;
   while (flag){
   
       if(cadena[count->n]=='I'){
          count->s=0;
          count->n=count->n+2;
          do{
              count->n++;
              check->ip_s[count->s]=cadena[count->n];
              count->s++;
              if(cadena[count->n+1]=='.')
                  count->c++;
          }while(count->c<4);
          count->s=0;
          count->n++;
          while(cadena[count->n+1]!=' '){
             count->n++;
             check->port_s[count->s]=cadena[count->n];
             count->s++; 
         }
      }
       
      
       if(cadena[count->n]=='>'){
          count->c=0;
          count->s=0;
          count->n++;
          do{
              count->n++;
              check->ip_d[count->s]=cadena[count->n];
              count->s++;
              if(cadena[count->n+1]=='.')
                  count->c++;
          }while(count->c<4);
          count->s=0;
          count->n++;
          while(cadena[count->n+1]!=':'){
             count->n++;
             check->port_d[count->s]=cadena[count->n];
             count->s++; 
         }
      }
      else if(cadena[count->n]=='['){
          switch(cadena[count->n+1]){
              case 'R': {check->flag=RST_ACK;break;}
              case 'S': {check->flag=SYNC_ACK;break;}
              case 'F': {check->flag=FIN_ACK;break;}
              case 'P': {check->flag=PUSH_ACK;break;}
          }
      }
          
      else if(cadena[count->n]=='q'){
          count->s=0;
          count->n++;
          if(check->flag!=PUSH_ACK){
              while(cadena[count->n+1]!=','){
                  count->n++;
                  check->seq[count->s]=cadena[count->n];
                  count->s++; 
              }
          }
          else{
              while(cadena[count->n+1]!=':'){
                  count->n++;
                  check->seq[count->s]=cadena[count->n];
                  count->s++; 
              }
               count->s=0;
               count->n++;
               while(cadena[count->n+1]!=','){
                   count->n++;
                   check->n_seq[count->s]=cadena[count->n];
                   count->s++; 
               }  
          }
      }
  
      else if(check->flag==RST_ACK)
          flag=false;
      
      else if(cadena[count->n]=='k'){
          count->s=0;
          count->n++;
          while(cadena[count->n+1]!=','){
             count->n++;
             check->ack[count->s]=cadena[count->n];
             count->s++; 
          }
          flag=false;
      }
      count->n++;
  }
   std::cout<<std::endl;
   std::cout<<"La Ip fuente: "<<check->ip_s<<std::endl;
   std::cout<<"El Puerto fuente: "<<check->port_s<<std::endl;
   std::cout<<"La Ip destino: "<<check->ip_d<<std::endl;
   std::cout<<"El Puerto destino : "<<check->port_d<<std::endl;
   std::cout<<"La Seq: "<<check->seq<<std::endl;
   std::cout<<"EL N_Seq: "<<check->n_seq<<std::endl;
   std::cout<<"El Ack: "<<check->ack<<std::endl;
   std::cout<<"El Flag: "<<int(check->flag)<<std::endl;

}

void package_assambler(tcp_dat*check,Message_queue&trigger)
{
        bzero((char *) &trigger, sizeof(trigger));
        trigger.package.id_client.sin_addr.s_addr=inet_addr(check->ip_d);
        trigger.package.id_client.sin_port=htons(short(atoi(check->port_d)));
        trigger.package.sequence_number=atoi(check->seq);
        trigger.package.acknowledge_number=atoi(check->ack);
        trigger.cli_addr.sin_addr.s_addr=inet_addr("127.0.0.1");
        trigger.cli_addr.sin_port=htons(QUIMERA_PORT); 
}